// Copyright (c) 2024 刻BITTER
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.


#pragma once

#include <cstdint>
#include <cstdlib>


namespace data_basic {

    /**
     * @brief 每个数据块的头部是用来串联所有数据快的链表节点指针
     *
     */
    template <typename DataType>
    union LinkedPoolNode {
        LinkedPoolNode *next;
        DataType data;
    };


    template <typename T, size_t PoolSize>
    class StaticBlockPool {
       public:
        using DataType = T;
        using DataNode = LinkedPoolNode<DataType>;

        // 数据块的尺寸必须大于一个指针的尺寸
        static_assert(sizeof(DataType) >= sizeof(DataType *));

       private:
        DataNode *_free_list = nullptr;
        DataNode _memory_pool[PoolSize];

        void _init_free_list() {
            // 初始化后内存池中的节点按从后往前的顺序排列，free_list 指向数组中最后一个元素
            // 经过使用后，由于节点归还的顺序不固定，所以链表的顺序与数组顺序不再相关
            for (size_t i = 0; i < PoolSize; i++) {
                this->_memory_pool[i].next = this->_free_list;  // 从末端节点开始逐步初始化空闲链表
                this->_free_list = &_memory_pool[i];
            }
        }


       public:
        StaticBlockPool() {
            this->_init_free_list();
        }

        size_t pool_size() const {
            return PoolSize;
        }

        bool empty() const {
            return _free_list == nullptr;
        }

        DataType *get_from_pool() {
            DataNode *node = _free_list;
            if (_free_list != nullptr) {
                _free_list = _free_list->next;  // 空闲链表的节点存取都发生在头节点
            }
            // return &node->data;
            return static_cast<DataType *>(node);
        }

        void return_to_pool(DataType *data) {
            DataNode *node = static_cast<DataNode *>(data);
            node->next = this->_free_list;
            this->_free_list = node;
        }

        // 禁止拷贝
        StaticBlockPool(const StaticBlockPool &) = delete;
        StaticBlockPool &operator=(const StaticBlockPool &) = delete;
    };


    // TODO: 再设计个全动态的伪内存池，新增节点用malloc，释放用free
    //       以及动态膨胀的内存池，新增的节点用malloc，回收节点用链表串起来备用，只获取内存，不释放，从而不会产生内存碎片
    //       动态膨胀的内存池可以打印日志，用来分析程序需要的内存池最大尺寸

    // TODO: DynamicMemoryPool
    // 动态内存池，有一个“不收缩”的选项，即内存池容量只增不减。内存池内只存储一种数据类型。
    //


}  // namespace data_basic
